(module controller mzscheme
  (provide (all-defined))
  
  (require (lib "class.ss")
           (lib "defmacro.ss")
           (lib "list.ss")
           "model/model.ss"
           "view/view.ss"
           "view/gui.ss")
  
  (define +log-file+ "log.srl")
  (define +course-file+ "course.srl")
  
  (define *run-log* (read-sexpr +log-file+))
  (define *courses* (read-sexpr +course-file+))
  
  (define *last-selection* #f)
  
  (define (save-logs)
    (save-sexpr *run-log* +log-file+)
    (save-sexpr *courses* +course-file+))
  
  (define (load-logs)
    (load-sexpr *run-log* log)
    (load-sexpr *courses* course))
  
  (define-macro input-handler
    (lambda (name)
      `(define ,name (send input ,name))))
  
  (define view (new srl-view% (label +name+)))
  (send view show #t)
  
  (define weekly-graph (send view weekly-graph))
  (define (weekly-initial)
    (define (w-i-helper log last-date so-far)
      (cond ((null? log) so-far)
            ((week-of? (car log) last-date)
             (w-i-helper (cdr log) last-date so-far))
            (else (w-i-helper (cdr log) (car log)
                              (append so-far
                                      (list (cons (run-date->ordinal-week (car log))
                                                  (weekly-mileage (car log) *run-log* *courses*))))))))
    (if (null? *run-log*)
        (list (cons 0 0))
        (let ((selected-year (if *last-selection*
                                 (third (car (list-ref *run-log* *last-selection*)))
                                 (get-year))))
          (w-i-helper 
           (map car (filter (lambda (x) (= selected-year (third (car x)))) *run-log*)) '(0 0 0) '()))))
  (send weekly-graph set-initial-points weekly-initial)
  
  (define total-graph (send view total-graph))
  
  (define input (send view input))
  (input-handler month)
  (input-handler day)
  (input-handler year)
  (input-handler time-of-day)
  (input-handler course)
  (input-handler distance)
  (input-handler hours)
  (input-handler minutes)
  (input-handler seconds)
  (input-handler pace)
  (input-handler hr)
  (input-handler notes)
  
  (define (get-today)
    "get-today sets the relevant fields and is a button handler"
    (let ((date (get-date)))
      (send month set-value (car date))
      (send day set-value (cadr date))
      (send year set-value (caddr date))))
  
  (define (load-pace)
    "load-pace calculates the pace given and loads it."
    (let ((rpace (pace-minutes/mile (string->number (send distance get-value))
                                    (make-run-time (string->number (send hours get-value))
                                                   (string->number (send minutes get-value))
                                                   (string->number (send seconds get-value))))))
      (send pace set-value (run-time->string rpace))))
  
  (send input set-on-today get-today)
  (send input set-on-pace-calculate load-pace)
  
  (define (load-item entry)
    (send month set-value (number->string (caar entry)))
    (send day set-value (number->string (cadar entry)))
    (send year set-value (number->string (caddar entry)))
    (send time-of-day set-value (cadr entry))
    (send course set-value (caddr entry))
    (send distance set-value (number->string (get-distance entry *courses*)))
    (send hours set-value (number->string (car (fifth entry))))
    (send minutes set-value (number->string (cadr (fifth entry))))
    (send seconds set-value (number->string (caddr (fifth entry))))
    (send hr set-value (number->string (sixth entry)))
    (send notes set-value (seventh entry))
    (load-pace))
  
  (define (form->run-date)
    "form->run-date returns a run-date built from the form values"
    (make-run-date (string->number (send month get-value))
                   (string->number (send day get-value))
                   (string->number (send year get-value))))
  
  (define (form->run-time)
    "form->run-time returns a run-time built from the form values"
    (make-run-time (string->number (send hours get-value))
                   (string->number (send minutes get-value))
                   (string->number (send seconds get-value))))
  
  (define (form->course)
    "form->course returns a course name or value built from the form value"
    (let ((value (send course get-value)))
      (cond ((string=? value "") #f)
            (else value))))
  
  (define (form->distance)
    "form->course returns a distance built from the form value"
    (cond ((defined-course? (form->course) *courses*) #t) ; If already defined
          ((not (form->course)) ; If empty
           (string->number (send distance get-value)))
          (else (begin (set! *courses*
                             (reverse
                              (cons
                               (list (form->course) (string->number (send distance get-value)))
                               *courses*)))
                       #t))))
  
  (define (form->entry)
    "form->entry returns a run-entry built from the form values"
    (make-run-entry (form->run-date)
                    (send time-of-day get-value)
                    (form->course)
                    (form->distance)
                    (form->run-time)
                    (string->number (send hr get-value))
                    (send notes get-value)))
  
  (define (remove-item)
    "remove-item removes an item matching that selected in the from list-box"
    (set! *run-log*
          (remove-entry
           (list-ref *run-log* *last-selection*)
           *run-log*))
    (send chooser set (chooser-list))
    (save-logs))
  
  (define (add-item)
    "add-item adds an item with values in the fields to to"
    (set! *run-log*
          (reverse (cons (form->entry) *run-log*)))
    (send chooser set (chooser-list))
    (save-logs))
  
  (define (chooser-display)
    (let ((entry (list-ref *run-log* (send chooser get-selection))))
      (set! *last-selection* (send chooser get-selection))
      (load-item entry)
      (send mpw-message set-label
            (number->string (weekly-mileage (car entry) *run-log* *courses*)))
      (send chooser set (chooser-list))))
  
  (define (chooser-list)
    (set! *run-log* (run-date-sort *run-log*))
    (list-of (lambda (entry)
               (string-append (number->string (caar entry)) "/"
                              (number->string (cadar entry)) "/"
                              (number->string (caddar entry)) " ("
                              (cadr entry) ")"))
             (run-date-sort *run-log*)))
  
  (define output (send view output))
  (define chooser (send output chooser))
  (define mpw-message (send output mpw))
  (send output set-on-add-entry add-item)
  (send output set-on-remove-entry remove-item)
  (send output set-on-edit-entry (lambda () (begin (remove-item) (add-item))))
  (send output set-on-choose chooser-display)
  
  (send chooser set (chooser-list)))